/**
 * @(#)CodeEntityTextAnalyzer.java, 2008-4-30.
 * Copyright 2008  Program Analysis Research Group(PARG),Institute of Software, Peking University.
 * All rights reserved.
 */
package edu.pku.sei.pgie.analyzer.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import edu.pku.sei.pgie.analyzer.core.Context;
import edu.pku.sei.pgie.analyzer.core.ICodeEntityConstants;
import edu.pku.sei.pgie.analyzer.core.ICollector;
import edu.pku.sei.pgie.analyzer.core.SourceTreeVisitor;
import edu.pku.sei.pgie.beans.CodeEntity;

/**
 * @author HeLi
 */
public class CodeEntityTextAnalyzer extends SourceTreeVisitor implements
        ICodeEntityConstants {

    ASTParser parser;

    IJavaProject javaProject;

    CodeEntitySymbolTable symbolTable;

    ICollector collector;

    Context context;

    public CodeEntityTextAnalyzer(Context context) throws Exception {
        super();
        this.context = context;
        javaProject = context.getJavaProject();
        collector = context.getCollector();
        parser = ASTParser.newParser(AST.JLS3);
        parser.setProject(javaProject);
        symbolTable = new CodeEntitySymbolTable();
    }

    class TextRecorder extends VisitorWithContext {
        String sourceCode;

        String sourceLocation;

        final List<CodeEntity> entityList;

        public TextRecorder(CompilationUnit root, String location)
                throws Exception {
            super(false);
            IJavaElement javaElement = root.getJavaElement();
            if (javaElement instanceof ISourceReference) {
                ISourceReference sourceReference = (ISourceReference) javaElement;
                sourceCode = sourceReference.getSource();
                entityList = new ArrayList<CodeEntity>();
                sourceLocation = location;
            } else {
                throw new Exception("Need Source Reference Interface");
            }
        }

        public List getEnityList() {
            return new ArrayList<CodeEntity>(entityList);
        }

        public boolean visit(MethodDeclaration node) {
            super.visit(node);
            if (context.isEnableMethodAnalyze()) {
                String fqn = peekMethodFQN();
                if (fqn == null)
                    return true;
                String context = sourceCode.substring(node.getStartPosition(),
                        node.getStartPosition() + node.getLength());

                CodeEntity entity = new CodeEntity();
                entity.setCodeId(collector.nextCodeId());
                entity.setFqn(fqn);
                entity.setEntityType(ENTITY_TYPE_METHOD);
                entity.setLocation(sourceLocation);
                entity.setContext(context);
                entityList.add(entity);
            }
            return true;
        }

        public boolean visit(TypeDeclaration node) {
            if (node.getParent().getNodeType() != ASTNode.COMPILATION_UNIT) {
                return false;
            }
            super.visit(node);
            ITypeBinding typeBinding = node.resolveBinding();
            if (typeBinding == null)
                return true;
            String fqn = typeBinding.getQualifiedName();

            CodeEntity entity = new CodeEntity();
            entity.setFqn(fqn);
            if (node.isInterface()) {
                entity.setEntityType(ENTITY_TYPE_INTERFACE);
            } else {
                entity.setEntityType(ENTITY_TYPE_CLASS);
            }
            // entity.setContext(context);
            entity.setCodeId(collector.nextCodeId());
            entity.setLocation(sourceLocation);
            entityList.add(entity);
            return true;
        }

    }

    public boolean visit(ICompilationUnit compilationUnit) {
        if (compilationUnit == null)
            return false;
        parser.setSource(compilationUnit);
        parser.setResolveBindings(true);
        CompilationUnit root = (CompilationUnit) parser.createAST(null);
        IPath sourcePath = compilationUnit.getResource().getFullPath();
        TextRecorder recorder = null;
        try {
            recorder = new TextRecorder(root, sourcePath.toString());
            root.accept(recorder);
            List entityList = recorder.getEnityList();
            Iterator iterator = entityList.iterator();
            while (iterator.hasNext()) {
                CodeEntity entity = (CodeEntity) iterator.next();
                if (collector != null) {
                    collector.collect(entity);
                }
                if (entity != null) {
                    symbolTable.put(entity.getFqn(), entity);
                }
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public CodeEntitySymbolTable getSymbolTable() {
        return symbolTable;
    }
}
